#pragma once
#include "dmalgo.h"

const long  DM_INT_VALUE_MISSING = 0xFFF80000L; 
const long  DM_INT_VALUE = 0xFFF80001L; 


class DMStateValue 
{
public:
	// Constructors
	__forceinline		  DMStateValue()		{ SetMissing(); }

	// Operators
	__forceinline operator DM_State() const			{ return IndexVal(); }
	__forceinline operator double() const			{ return DoubleVal(); }
	__forceinline operator DM_STATE_VALUE&()		{ return m_val; }
	__forceinline DMStateValue & operator =(DM_STATE_VALUE&val)	{ m_val = val; return *this; }
	__forceinline bool operator ==(DM_STATE_VALUE&val)	{ return m_val.r8 == val.r8; }


	// Accessors
	__forceinline bool     IsMissing() const		{ return IsMissing(m_val); }
	__forceinline bool     IsDouble()  const		{ return IsDouble(m_val); }
	__forceinline bool     IsIndex()   const		{ return IsIndex(m_val); }
	__forceinline DM_State IndexVal()  const		{ return IndexVal(m_val); }
	__forceinline double   DoubleVal() const		{ return DoubleVal(m_val); }
	__forceinline void	   SetMissing()				{ SetMissing(m_val); }
	__forceinline void     SetDouble(double r8)		{ SetDouble(m_val, r8);	}
	__forceinline void     SetIndex(ULONG index)	{ SetIndex(m_val, index); }


	static __forceinline bool		IsMissing(const DM_STATE_VALUE & val) 		{ return (val.si.iFlag == (ULONG)DM_INT_VALUE_MISSING) || (IsIndex(val) && (0==IndexVal(val))); }
	static __forceinline bool		IsDouble(const DM_STATE_VALUE & val)  		{ return !IsIndex(val) && !IsMissing(val);	}
	static __forceinline bool		IsIndex(const DM_STATE_VALUE & val)	  		{ return (val.si.iFlag == DM_INT_VALUE); }
	static __forceinline DM_State	IndexVal(const DM_STATE_VALUE & val)  		{ return val.si.iState;	}
	static __forceinline DM_State	GetState(const DM_STATE_VALUE & val)  		{ ATLASSERT(IsMissing(val) || IsIndex(val)); return IsMissing(val) ? 0 : IndexVal(val); }
	static __forceinline double		DoubleVal(const DM_STATE_VALUE & val) 		{ return val.r8; }
	static __forceinline void		SetMissing(DM_STATE_VALUE & val)				{ val.si.iFlag = DM_INT_VALUE_MISSING; val.si.iState = 0; }
	static __forceinline void		SetDouble(DM_STATE_VALUE & val, double r8)		{ val.r8 = r8;	}
	static __forceinline void		SetIndex(DM_STATE_VALUE & val, DM_State index)	{ val.si.iFlag = DM_INT_VALUE; val.si.iState = index;	}

	DM_STATE_VALUE m_val;
};

class DMAttributeValue
{
public:
	__forceinline		DMAttributeValue()	{ m_att.Attribute = (DM_Attribute)-1; DMStateValue::SetMissing(m_att.Value); }

	__forceinline		DMStateValue & StateValue()	{ return *(DMStateValue*)&m_att.Value; }
	__forceinline		DM_Attribute & Attribute() { return m_att.Attribute; }


	DM_ATTRIBUTE_VALUE m_att;
};




// ------------------------------------------------------------------------
// DMCaseItr:
// This is a helper class for iterating cases.
// For each case, it supports iterating forward over the attribute value 
// pairs of a case.
// This provides insulation from the sparse / dense storage issues while 
// still giving quick access to all of a case's values. 
// Only non-missing values are returned.
// ------------------------------------------------------------------------
class DMCaseItr
{
public:
	inline DMCaseItr()
	{
		DMStateValue::SetMissing(m_Missing);
	}
	inline HRESULT	SetCase(ULONG * pCaseData, ULONG cAtts, bool bDense = false)
	{
		if( cAtts == DM_UNSPECIFIED)
		{
			m_pCaseData	=	NULL;
			m_cAtts	=	-1;
			m_oAtt	=	-1;
		}
		else
		{
			m_pCaseData = pCaseData;
			m_bDense = bDense;
			m_ElSize = bDense? sizeof(DM_STATE_VALUE)/sizeof(ULONG) : sizeof(DM_ATTRIBUTE_VALUE)/sizeof(ULONG);
			m_cAtts = cAtts;
			m_oAtt	=	-1;
		}

		return ResetCase();
	}

	inline HRESULT	SetCase(DM_ATTRIBUTE_VALUE* pCaseData, ULONG cAtts)
	{

		return SetCase((ULONG*)pCaseData, cAtts, false);
	}

	inline bool fCaseEnd()								// true if after last attribute in case
	{
		return m_oAtt == m_cAtts;	
	}
	
	inline HRESULT	NextAttr(bool bSkipBadOutput = true)	// move to next attribute of current case
	{
		if (fCaseEnd())
			return S_FALSE;

		do
		{
			m_oAtt += 1;
		}
		while (!fCaseEnd() && (Value().IsMissing()) );
							//|| @todo feature selection
							//(bSkipBadOutput && m_pcasecache->m_spias &&
								//m_pcasecache->m_spias->IsBadOutputAtt(Att()) == S_OK)));
		return fCaseEnd()? S_FALSE : S_OK;
	}
	
	inline DM_Attribute	Att()									// number of current attribute
	{
		return m_bDense? m_oAtt : ((DMAttributeValue *)(m_pCaseData + m_oAtt * m_ElSize))->Attribute();
	}
	
	inline HRESULT		ResetCase()								// start attribute iteration over at first in case
	{
		m_oAtt = -1;
		return NextAttr();
	}

	inline	DMStateValue& DMCaseItr::Value(void) 
	{
		return m_bDense? *(DMStateValue*)(m_pCaseData + m_oAtt * m_ElSize): ((DMAttributeValue*)(m_pCaseData + m_oAtt * m_ElSize))->StateValue();
	}


	inline DM_State		IndexVal()								// long version of current attribute's value
	{
		return Value().IndexVal();
	}
	
	inline double			DoubleVal()							// float version of current attribute's value
	{
		return Value().DoubleVal();
	}

	inline DM_STATE_VALUE ValFromAttribute(DM_Attribute iAtt)
	{
		ULONG oAtt = m_oAtt;
		if (m_bDense)
		{
			if( iAtt < m_cAtts )
			{
				m_oAtt = iAtt;
				return Value();
			}
		}
		else
		{
			m_oAtt = -1;
			do
			{
				NextAttr();
			} while (!fCaseEnd() && Att() != iAtt);

			if (Att() == iAtt)
				return Value();
		}

		m_oAtt = oAtt;
		return m_Missing;
	}


protected:
	ULONG			m_oAtt;

	ULONG*			m_pCaseData;
	ULONG			m_cAtts;
	ULONG			m_ElSize;
	bool			m_bDense;
	bool			m_bSkipBadOutput;
	DM_STATE_VALUE	m_Missing;				// const value for missing


};
